package com.pic.mgt.syushin.manager.business;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.dao.DataAccessException;

import com.pic.mgt.dao.IBusinessListMgtDao;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.manager.business.ApInfoBean;
import com.pic.mgt.manager.business.BasicBusinessInfoBean;
import com.pic.mgt.manager.business.BusinessManagementOperationException;
import com.pic.mgt.manager.business.SimpleBusinessManager;
import com.pic.mgt.monitor.physicalmonitor.FileMonitor;
import com.pic.mgt.monitor.physicalmonitor.FileMonitorException;
import com.pic.mgt.monitor.physicalmonitor.FileMonitorStopException;
import com.pic.mgt.monitor.physicalmonitor.MonitoredFile;
import com.pic.mgt.monitor.physicalmonitor.FileMonitor.FileMonitorEvent;
import com.pic.mgt.syushin.action.SyushinBusinessApMgtAction;

public final class SimpleSyushinBusinessManager extends SimpleBusinessManager
		implements SyushinBusinessManager {
	
	private BusinessFolderDetection businessFolderDetection;
	
	private FileMonitor fileMonitor;
	
	// (送多方新增)
	private SyushinBusinessApMgtAction syushinBusinessApMgtAction;
	
	public SimpleSyushinBusinessManager(IBusinessListMgtDao businessListMgtDao,
										String syushinBatchTmpPathStr,
										int businessNameLen,
										FileMonitor fileMonitor) throws BusinessManagementOperationException {
		super(businessListMgtDao, syushinBatchTmpPathStr, businessNameLen);
		
		this.fileMonitor = fileMonitor;
		
		// 啟動業務目錄監控(以動態偵測業務是否新增或移除)
		try {
			// throws FileMonitorException
			this.businessFolderDetection = new BusinessFolderDetection();
			// throws FileMonitorException
			this.businessFolderDetection.startBusinessFolderDetection();
		} catch (FileMonitorException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-320 Starting monitor business folder (under syushin batch tmp folder) failed: " + e.getMessage());
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************
				
				throw e1;
			}
		}
	}
	
	@Override
	protected void synchronizeAddBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException {
		
		// 以資料庫中的業務為主。若業務不存在於清單中且有對應的目錄，則必須新增。
		
		Set<String> businessNameInDbSet = basicBusinessInfoBeanInDbMap.keySet();
		for (String businessNameInDb : businessNameInDbSet) {
			if (!super.basicBusinessInfoBeanMap.containsKey(businessNameInDb)) {
				// 若清單內沒有該端末，若此端末為有效端末且有對應的目錄存在，則必須新增。
				
				if (businessFolderNameMap.containsKey(businessNameInDb)) {
					doAddBusiness(basicBusinessInfoBeanInDbMap.get(businessNameInDb));
					
					// 同步新增"後"通知 TerminalListener
					this.businessManagementListener.
							afterSynchronizeAddedBusinessNotify(basicBusinessInfoBeanInDbMap.get(businessNameInDb));
				}
			}
		}
	}
	
	private void doAddBusiness(BasicBusinessInfoBean newBasicBusinessInfoBean) {
		// 新增至業務清單
		synchronized(super.basicBusinessInfoBeanMap) {
			super.basicBusinessInfoBeanMap.put(newBasicBusinessInfoBean.getBusinessName(),
											   newBasicBusinessInfoBean);
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Creating business '{}' operation completed.",
				newBasicBusinessInfoBean.getBusinessName());
		// ********************** Log **********************
	}
	
	@Override
	protected void synchronizeRemoveBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException {
		// 以清單中的業務為主。若業務不存在於資料庫中，則必須刪除。
		
		// 用來存放將要移除的業務名稱
		List<String> toBeRemovedBusinessNameList = new LinkedList<String>();
		
		Set<String> businessNameSet = super.basicBusinessInfoBeanMap.keySet();
		for (String businessName : businessNameSet) {
			if (!basicBusinessInfoBeanInDbMap.containsKey(businessName)) {
				// 先將要移除的端末ＩＤ放到List
				toBeRemovedBusinessNameList.add(businessName);
			}
		}
		
		// 開始做移除
		for (String toBeRemovedBusinessName : toBeRemovedBusinessNameList) {
			// 要刪除"前"先通知 BusinessListener
			this.businessManagementListener.
					beforeSynchronizeRemovedBusinessNotify(toBeRemovedBusinessName);
			
			doRemoveBusiness(toBeRemovedBusinessName);
		}
	}
	
	@Override
	public synchronized void addBusiness(String newBusinessName)
			throws BusinessManagementOperationException {
		// 集信只需要做的是新增至業務清單
		
		// 從資料庫中取出該已新增的業務基本資訊
		BasicBusinessInfoBean newBasicBusinessInfoBean;
		try {
			// throws DataAccessException
			newBasicBusinessInfoBean = super.businessListMgtDao.
							findBasicBusinessInfoByBusinessName(newBusinessName);
		} catch (DataAccessException e) {
			// ********************** Log **********************
			IgxLog.messageDetailsLog.error(this, e.getMessage());
			// ********************** Log **********************
			
			throw new BusinessManagementOperationException(
					"Adding business '" + newBusinessName + "'failed. Due to database operation error." ,e);
		}
		if (null == newBasicBusinessInfoBean) {
			throw new BusinessManagementOperationException(
					"Adding business '" + newBusinessName + "' failed. Can't find new terminal '" +
					newBusinessName + "' in database (table: " +
					super.businessListMgtDao.getTableName() + ")");
		}
		
		doAddBusiness(newBasicBusinessInfoBean);
		
		// 動態新增"後"通知 BusinessListener
		this.businessManagementListener.
				afterDynamicAddedBusinessNotify(newBasicBusinessInfoBean);
	}
	
	@Override
	public synchronized void removeBusiness(String toBeRemovedBusinessName)
			throws BusinessManagementOperationException {
		// 集信只需要做的是從業務清單中移除
		
		// 要刪除"前"先通知 BusinessListener
		this.businessManagementListener.
				beforeDynamicRemovedBusinessNotify(toBeRemovedBusinessName);
		
		doRemoveBusiness(toBeRemovedBusinessName);
	}
	
	private void doRemoveBusiness(String toBeRemovedBusinessName) {
		// 從業務清單中移除
		synchronized (super.basicBusinessInfoBeanMap) {
			super.basicBusinessInfoBeanMap.remove(toBeRemovedBusinessName);
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Removing business '{}' operation completed.", toBeRemovedBusinessName);
		// ********************** Log **********************
	}
	
	/**
	 * 此類別是集信系統用來偵測業務目錄的新增或移除,以便能夠得知業務已被新增或移除(因為業務
	 * 的管理動作皆是在配信系統上操作)。也就是集信系統要能夠即時得知業務的異動，必須要藉由對
	 * 業務資料匣的監控來達成。
	 * 1. 當業務資料匣被新增出來，表示配信系統完成了一項業務的新增作業。
	 * 2. 當業務資料匣被刪除，表示配信系統完成了一項業務的移除作業。
	 * @author p10118178
	 *
	 */
	private class BusinessFolderDetection implements Runnable {
		private boolean isStartedMonitor = false;
		
		public BusinessFolderDetection() throws FileMonitorException {
			// 監控目錄的'建立'和'刪除'事件
			fileMonitor.setMonitorEvent(FileMonitorEvent.FILE_CREATE, FileMonitorEvent.FILE_DELETE);
			
			// 新增目錄監控
			// throws FileMonitorException
			fileMonitor.addMonitorPath(syushinBatchTmpPathStr);
		}
		
		public void startBusinessFolderDetection() throws FileMonitorException  {
			if (isStartedMonitor) {
				return;
			}
			
			// 開始監控
			// throws FileMonitorException
			fileMonitor.startMonitor();
			
			// 啟動 thread 讀取監控到的檔案變動
			new Thread(this).start();
			
			isStartedMonitor = true;
			
			// ********************** Log **********************
			IgxLog.messageLog.info(SimpleSyushinBusinessManager.this,
					"Started monitor business folder (under syushin batch tmp folder).");
			// ********************** Log **********************
		}

		@Override
		public void run() {
			try {
				while(true) {
					List<MonitoredFile> monitoredFileList = fileMonitor.readMonitoredList();
					if (!monitoredFileList.isEmpty()) {
						for (MonitoredFile monitoredFile : monitoredFileList) {
							File detectedFile = monitoredFile.getChangedFile();
							FileMonitorEvent detectedEvent = monitoredFile.getFileChangedEvent();
							
							if (monitoredFile.isDirectory()) {
								String detectedBusinessName = detectedFile.getName().trim();
								
								if (detectedEvent == FileMonitorEvent.FILE_CREATE) {
									if (detectedBusinessName.length() == businessNameLen) {
										// ********************** Log **********************
										IgxLog.messageLog.info(this,
														"Detected a business folder '{}' creation.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
										
										// 做新增業務動作
										try {
											// throws BusinessManagementOperationException
											addBusiness(detectedBusinessName);
										} catch (BusinessManagementOperationException e) {
											// ********************** Log **********************
											IgxLog.messageLog.error(
															SimpleSyushinBusinessManager.this,
															e.getMessage());
											// ********************** Log **********************
										}
									}
									else {
										// 業務名稱長度不正確
										
										// ********************** Log **********************
										IgxLog.messageLog.warn(this,
														"Detected a business folder '{}' creation. But the length of business name is incorrect.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
									}
								}
								else if (detectedEvent == FileMonitorEvent.FILE_DELETE) {
									if (detectedBusinessName.length() == businessNameLen) {
										// ********************** Log **********************
										IgxLog.messageLog.info(this,
														"Detected a business folder '{}' deletion.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
										
										// 做移除業務動作
										try {
											// (送多方新增)
											// 刪除業務所有對應的AP端
											BasicBusinessInfoBean basicBusinessInfoBean = SimpleSyushinBusinessManager.this.getBasicBusinessInfoBean(detectedBusinessName);
											if (null != basicBusinessInfoBean) {
												boolean isDeleteSuccess =
														deleteAllAPByBusiness(basicBusinessInfoBean);
												if (!isDeleteSuccess) {
													// ********************** Log **********************
													IgxLog.messageLog.error(this, 
															"Failure to remove AP destinations from business '{}'.", detectedBusinessName);
													// ********************** Log **********************
												}
											}
											
											// throws BusinessManagementOperationException
											removeBusiness(detectedBusinessName);
										} catch (BusinessManagementOperationException e) {
											// ********************** Log **********************
											IgxLog.messageLog.error(
															SimpleSyushinBusinessManager.this,
															e.getMessage());
											// ********************** Log **********************
										}
									}
									else {
										// 業務名稱長度不正確
										
										// ********************** Log **********************
										IgxLog.messageLog.warn(this,
														"Detected a business folder '{}' deletion. But the length of business name is incorrect.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
									}
								}
							}
							else {
								// 檔案不做處理
							}
						}
					}
				}
			} catch (FileMonitorStopException e) {
				// ********************** Log **********************
				IgxLog.messageLog.info(this, "Stopped monitor business folder (under syushin batch tmp folder).");
				// ********************** Log **********************
			}
		}
		
		// (送多方新增)
		private boolean deleteAllAPByBusiness(BasicBusinessInfoBean basicBusinessInfoBean) {
			ApInfoBean[] apInfoBeanArray = new ApInfoBean[basicBusinessInfoBean.getSyushinBusinessApList().size()];
			basicBusinessInfoBean.getSyushinBusinessApList().toArray(apInfoBeanArray);
			
			for (ApInfoBean apInfoBean : apInfoBeanArray) {
				boolean isDeleteSuccess =
						SimpleSyushinBusinessManager.this.syushinBusinessApMgtAction.deleteSyushinBusinessAp(
													basicBusinessInfoBean.getBusinessName(),
													apInfoBean.getApName());
				if (!isDeleteSuccess) {
					return false;
				}
			}
			return true;
		}
	}

	// --- getter and setter ---

	// (送多方新增)
	public void setSyushinBusinessApMgtAction(
			SyushinBusinessApMgtAction syushinBusinessApMgtAction) {
		this.syushinBusinessApMgtAction = syushinBusinessApMgtAction;
	}

}
